/*
 * Duplicate Detector Copyright (C) 2010 Marco Biscaro <marcobiscaro2112@gmail.com>
 * 
 * This file is part of Duplicate Detector.
 *
 * Duplicate Detector is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Duplicate Detector is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Duplicate Detector.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.duplicatedetector.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;

import com.googlecode.duplicatedetector.Constants;

/**
 * File utilities class.
 * 
 * @author Marco Biscaro
 */
public final class FileUtils implements Constants {

	private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
	public static final int BUFFER_SIZE = 524288;

	private FileUtils() {
		// can not instantiate
	}

	public static ByteWrapper readFirstBytes(File f) {
		byte[] read = null;
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(f);
			FileChannel channel = fis.getChannel();
			ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
			int bytesRead = channel.read(buffer);
			if (bytesRead < 1) {
				read = new byte[0];
			} else {
				read = Arrays.copyOf(buffer.array(), bytesRead);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException ex2) {
					// do nothing
				}
			}
		}
		return new ByteWrapper(read);
	}

	public static String generateHash(File f) {
		try {
			MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
			InputStream is = null;
			try {
				is = new BufferedInputStream(new FileInputStream(f));
				byte[] buf = new byte[BUFFER_SIZE];
				for (int nBytes = is.read(buf, 0, buf.length); nBytes > 0; nBytes = is
						.read(buf, 0, buf.length)) {
					md.update(buf, 0, nBytes);
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			} finally {
				if (is != null) {
					try {
						is.close();
					} catch (IOException ex2) {
						// do nothing
					}
				}
			}
			byte[] digest = md.digest();
			return hex(digest);
		} catch (NoSuchAlgorithmException ex) {
			throw new RuntimeException("Can't happen", ex);
		}
	}

	public static String hex(byte[] bytes) {
		StringBuilder sb = new StringBuilder();
		for (byte b : bytes) {
			sb.append(HEX_DIGITS[(b & 0xF0) >>> 4])
					.append(HEX_DIGITS[b & 0x0F]);
		}
		return sb.toString();
	}

	public static boolean isSymlink(File file) {
		File canon;
		try {
			if (file.getParent() == null) {
				canon = file;
			} else {
				File canonDir;
				canonDir = file.getParentFile().getCanonicalFile();
				canon = new File(canonDir, file.getName());
			}
			return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
	}

	public static String getLength(File file) {
		return getLength(file.length());
	}

	public static String getLength(long length) {
		if (length < 1024) {
			return getAbsoluteLength(length);
		}
		if (length < 1048576) {
			return String.format("%.1f", length / 1024.0) + " KB ("
					+ getAbsoluteLength(length) + ")";
		}
		if (length < 1073741824) {
			return String.format("%.1f", length / 1048576.0) + " MB ("
					+ getAbsoluteLength(length) + ")";
		}
		return String.format("%.1f", length / 1073741824.0) + " GB ("
				+ getAbsoluteLength(length) + ")";
	}

	public static String getAbsoluteLength(File file) {
		return getAbsoluteLength(file.length());
	}

	public static String getAbsoluteLength(long length) {
		return length + " byte" + (length > 1 ? "s" : "");
	}

	public synchronized static String getLastModified(File file) {
		return DATE_FORMAT.format(new Date(file.lastModified()));
	}

}
